#include "server.h"

extern MYSQL* mySql;

int RecvFile(int peerFd, int userId){
    //定义应用层缓冲区和接收字节数，以及相关变量
    int length;
    char nameBuff[BUFFSIZE] = {0};
    char md5Buff[BUFFSIZE] = {0};
    char md5CmpBuff[BUFFSIZE] = {0};
    off_t byteHaveRecv = 0;
    char querySentence[2048] = {0};
    int exist = 0;
    int type = 2;
    //接收文件名
    int ret = recv(peerFd, &length, 4, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        return -1;
    }
    memset(nameBuff, 0, BUFFSIZE);
    ret = recv(peerFd, nameBuff, length, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        return -1;
    }
    printf("%s\n", nameBuff);
    //接收md5码
    ret = recv(peerFd, &length, 4, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        return -1;
    }
    memset(md5Buff, 0, BUFFSIZE);
    ret = recv(peerFd, md5Buff, length, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        return -1;
    }
    printf("%s\n", md5Buff);
    //接收文件大小
    off_t fileSize;
    ret = recv(peerFd, &fileSize, sizeof(off_t), MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        return -1;
    }
    //先查询在服务器本地该目录下是否有同名文件，有的话发送-1，并结束传输
    //先把要传输的文件名从路径中分离出来
    char fileName[1024] = {0};
    char pathName[1024] = {0};
    length = strlen(nameBuff) - 1;
    if(length < 0) {
        return -1;
    }
    if(nameBuff[length] == '/') {
        nameBuff[length] = 0;
        length--;
    }
    if(length < 0) {
        return -1;
    }
    int i;
    for(i = length; i >= -1; i--) {
        if(i < 0 || nameBuff[i] == '/') {
            break;
        }
    }
    strcpy(fileName, nameBuff + i + 1);
    if(i >= 0) {
        memcpy(pathName, nameBuff, i);
    }
    //分析路径，获得目录id
    int fileId;
    ret = PathnameAnalyse(pathName, userId, &fileId);
    //如果创建目录的路径正确且最后一个分片为目录，则检查是否有同名的文件或目录，如果存在则获取其md5码
    if(ret == DIR_NAME) {
        sprintf(querySentence,
                "select md5 from filesystem where parent_id = %d and fileName = '%s'", 
                fileId, fileName);
        ret = mysql_query(mySql, querySentence);
        if(ret != 0) {
            printf("query error: %s\n", mysql_error(mySql));
            return -1;
        }
        MYSQL_RES* result = mysql_store_result(mySql);
        if(result == NULL) {
            printf("query error: %s\n", mysql_error(mySql));
            return -1;
        }
        MYSQL_ROW row = mysql_fetch_row(result);
        if(row == NULL) {
            exist = 0;
        } else {
            exist = 1;
            strcpy(md5CmpBuff, row[0]);
        }
        mysql_free_result(result);
        //开始判断
        if(exist == 0) {
            //如果不存在，则直接创建目录项
            memset(querySentence, 0, sizeof(querySentence));
            sprintf(querySentence, 
                    "insert into filesystem (parent_id, filename, owner_id, md5, filesize, type) values (%d, '%s', %d, '%s', 0, 2)", 
                    fileId, fileName, userId, md5Buff);
            ret = mysql_query(mySql, querySentence);
            if(ret != 0) {
                printf("query error: %s\n", mysql_error(mySql));
                return -1;
            }
            //查找是否有md5码相同的文件，如果有，判断需不需要续传
            memset(querySentence, 0, sizeof(querySentence));
            sprintf(querySentence,
                    "select filesize, type from filesystem where md5 = '%s'", 
                    md5Buff);
            ret = mysql_query(mySql, querySentence);
            if(ret != 0) {
                printf("query error: %s\n", mysql_error(mySql));
                return -1;
            }
            MYSQL_RES* result = mysql_store_result(mySql);
            if(result == NULL) {
                printf("query error: %s\n", mysql_error(mySql));
                return -1;
            }
            MYSQL_ROW row = mysql_fetch_row(result);
            if(row != NULL) {
                //有md5码相同的文件，记录其type和filesize
                byteHaveRecv = atoi(row[0]);
                type = atoi(row[1]);
            }
            mysql_free_result(result);
            //修改该文件目录项的值
            memset(querySentence, 0, sizeof(querySentence));
            sprintf(querySentence,
                    "update filesystem set filesize = %ld, type = %d where md5 = '%s'", 
                    byteHaveRecv, type, md5Buff);
            ret = mysql_query(mySql, querySentence);
            if(ret != 0) {
                printf("query error: %s\n", mysql_error(mySql));
                return -1;
            }
            //type为1表示该物理文件已经存在，不需要再传，发送-2
            if(type == 1) {
                off_t flag = -2;
                Sendn(peerFd, &flag, sizeof(off_t));
                return 0;
            }
        } else {
            //如果该目录下存在同名文件，先判断其md5码和要接收文件的md5码是否相同，不同则发送-1
            //相同则判断该文件是否已经传输完毕，已经传输完毕则修改目录项的值并发送-2，否则续传
            if(strcmp(md5Buff, md5CmpBuff) != 0) {
                off_t flag = -1;
                Sendn(peerFd, &flag, sizeof(off_t));
                return 0;
            } else {
                memset(querySentence, 0, sizeof(querySentence));
                sprintf(querySentence,
                        "select filesize, type from filesystem where md5 = '%s'", 
                        md5Buff);
                ret = mysql_query(mySql, querySentence);
                if(ret != 0) {
                    printf("query error: %s\n", mysql_error(mySql));
                    return -1;
                }
                MYSQL_RES* result = mysql_store_result(mySql);
                if(result == NULL) {
                    printf("query error: %s\n", mysql_error(mySql));
                    return -1;
                }
                MYSQL_ROW row = mysql_fetch_row(result);
                if(row != NULL) {
                    //记录其type和filesize
                    byteHaveRecv = atoi(row[0]);
                    type = atoi(row[1]);
                }
                mysql_free_result(result);
                if(type == 1) {
                    off_t flag = -2;
                    Sendn(peerFd, &flag, sizeof(off_t));
                    return 0;
                }
            }
        }
    } else {
        //路径错误，发送-1
        off_t flag = -1;
        Sendn(peerFd, &flag, sizeof(off_t));
        return 0;
    }

    //在本地打开或者创建文件，文件名为md5码
    char trueFilename[1024] = {0};
    sprintf(trueFilename, "file/%s", md5Buff);
    int fd = open(trueFilename, O_RDWR | O_CREAT, 0666);
    if(fd == -1) {
        perror("open");
        return -1;
    }

    //发送已接收的文件长度
    Sendn(peerFd, &byteHaveRecv, sizeof(off_t));

    //使用零拷贝接收文件
    //每接收100M，数据库对应filesize更新一次
    ftruncate(fd, fileSize);
    off_t byteNeedRecv = fileSize - byteHaveRecv;
    off_t byteNeedRecvNow = byteNeedRecv;
    off_t byteHaveRecvNow = byteHaveRecv;
    printf("%ld, %ld, %ld\n", fileSize, byteHaveRecv, byteNeedRecv);
    char* pbuf = (char*)mmap(NULL, byteNeedRecv, PROT_READ | PROT_WRITE, MAP_SHARED, fd, byteHaveRecv);
    long pbufNow = 0;
    if(pbuf == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    while(byteNeedRecvNow > _100M) {
        ret = recv(peerFd, pbuf + pbufNow, _100M, MSG_WAITALL);
        if(ret < 0){
            perror("recv");
            munmap(pbuf, byteNeedRecv);
            close(fd);
            return -1;
        } else if(ret == 0) {
            munmap(pbuf, byteNeedRecv);
            close(fd);
            return -1;
        }
        byteNeedRecvNow -= _100M;
        byteHaveRecvNow += _100M;
        pbufNow += _100M;
        memset(querySentence, 0, sizeof(querySentence));
        sprintf(querySentence,
                "update filesystem set filesize = %ld where md5 = '%s'", 
                byteHaveRecvNow, md5Buff);
        ret = mysql_query(mySql, querySentence);
        if(ret != 0) {
            printf("query error: %s\n", mysql_error(mySql));
            munmap(pbuf, byteNeedRecv);
            close(fd);
            return -1;
        }
    }
    //将剩余不到100M的数据传输完成，更新数据库，把文件的type改成1
    ret = recv(peerFd, pbuf + pbufNow, byteNeedRecvNow, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        munmap(pbuf, byteNeedRecv);
        close(fd);
        return -1;
    } else if(ret == 0) {
        munmap(pbuf, byteNeedRecv);
        close(fd);
        return -1;
    }
    type = 1;
    byteHaveRecvNow += byteNeedRecvNow;
    memset(querySentence, 0, sizeof(querySentence));
    sprintf(querySentence,
            "update filesystem set filesize = %ld, type = %d where md5 = '%s'", 
            byteHaveRecvNow, type, md5Buff);
    ret = mysql_query(mySql, querySentence);
    if(ret != 0) {
        printf("query error: %s\n", mysql_error(mySql));
        munmap(pbuf, byteNeedRecv);
        close(fd);
        return -1;
    }
    munmap(pbuf, byteNeedRecv);
    close(fd);
    return 0;
}

